#include "elbutton.h"
#include <QDesktopServices>
#include <QEnterEvent>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QPainter>
#include "elicon.h"

const int ElButton::Icon_Size = 16;
const int ElButton::Padding_Lr = 20;
const int ElButton::Padding_Tb = 8;
const int ElButton::Spacing = 4;

ElButton::ElButton(QWidget *parent)
    : ElAbstractButton{parent}
{
    initPalette();
}

ElButton::ElButton(const QString &text, QWidget *parent)
    : ElButton{parent}
{
    setText(text);
}

void ElButton::setTheme(ElTheme::Type type)
{
    _theme.setType(type);
    initPalette();
    if (_icon != nullptr)
        _icon->fillBackground(_palette.color(ElPalette::Foreground));
    update();
}

ElTheme ElButton::theme() const
{
    return _theme;
}

ElButton::Type ElButton::type() const
{
    return _type;
}

void ElButton::setType(Type type)
{
    _type = type;
    update();
}

bool ElButton::event(QEvent *e)
{
    if (e->type() == QEvent::EnabledChange) {
        auto colorGroup = isEnabled() ? ElPalette::Normal : ElPalette::Disabled;
        _palette.toggleColorGroup(colorGroup);
        if (_icon != nullptr)
            _icon->fillBackground(_palette.color(ElPalette::Foreground));
        update();
    }
    return ElAbstractButton::event(e);
}

void ElButton::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    QRectF br;
    painter.drawText(QRectF(), Qt::AlignCenter, _text, &br);
    bool hasIcon = _icon != nullptr;
    auto w = 2 * Padding_Lr + (hasIcon ? Icon_Size + Spacing : 0) + br.width();
    auto h = 2 * Padding_Tb + br.height();
    resize(w, h);
    // draw rect
    QPen pen;
    pen.setColor(_palette.color(ElPalette::Border));
    painter.setPen(pen);
    painter.setBrush(_palette.color(ElPalette::Background));
    qreal r = (_type != Rounded) ? height() / 6.0 : height() / 2.0;
    painter.drawRoundedRect(getDrawRect(), r, r);
    painter.setPen(_palette.color(ElPalette::Foreground));
    // draw text
    auto left = _iconStyle == Append ? Padding_Lr : width() - Padding_Lr - br.width();
    QRectF drawing_rect(left, 0, br.width(), height());
    painter.drawText(drawing_rect, Qt::AlignCenter, _text);
    // draw icon
    if (hasIcon) {
        auto iconLeft = _iconStyle == Append ? width() - Padding_Lr - Icon_Size : Padding_Lr;
        _icon->move(iconLeft, (height() - Icon_Size) / 2);
        _icon->show();
    }

    e->accept();
}

void ElButton::enterEvent(QEnterEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    showTooltip();
    _palette.toggleColorGroup(ElPalette::Entered);
    if (_icon != nullptr)
        _icon->fillBackground(_palette.color(ElPalette::Foreground));
    update();
    e->accept();
}

void ElButton::leaveEvent(QEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    hideTooltip();
    _palette.toggleColorGroup(ElPalette::Normal);
    if (_icon != nullptr)
        _icon->fillBackground(_palette.color(ElPalette::Foreground));
    update();
    e->accept();
}

void ElButton::mousePressEvent(QMouseEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Pressed);
    if (_icon != nullptr)
        _icon->fillBackground(_palette.color(ElPalette::Foreground));
    update();
    emit pressed();
    e->accept();
}

void ElButton::mouseReleaseEvent(QMouseEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Entered);
    if (_icon != nullptr)
        _icon->fillBackground(_palette.color(ElPalette::Foreground));
    update();
    emit released();
    auto pos = e->pos();
    if (pos.x() >= 0 && pos.y() >= 0 && pos.x() <= width() && pos.y() <= height())
        emit clicked();
    e->accept();
}

void ElButton::initPalette()
{
    auto normal = _theme.normalColor(), shallow = _theme.shallowColor();
    auto deep = _theme.deepColor();
    auto entered = _theme.enteredColor(), disabled = _theme.disabledColor();
    bool isDefault = (_theme.type() == ElTheme::Default), isPlain = (_type == Plain);

    if (isDefault) {
        _palette.setColor(ElPalette::Background, Qt::white);
        _palette.setColor(ElPalette::Foreground, ThemeColor::textColor);
        _palette.setColor(ElPalette::Border, ThemeColor::lineColor);
    } else if (isPlain) {
        _palette.setColor(ElPalette::Background, shallow);
        _palette.setColor(ElPalette::Foreground, normal);
        _palette.setColor(ElPalette::Border, normal);
    } else {
        _palette.setColor(ElPalette::Background, normal);
        _palette.setColor(ElPalette::Foreground, Qt::white);
        _palette.setColor(ElPalette::Border, normal);
    }

    _palette.toggleColorGroup(ElPalette::Entered);
    if (isDefault) {
        _palette.setColor(ElPalette::Background, isPlain ? Qt::white : shallow);
        _palette.setColor(ElPalette::Foreground, normal);
        _palette.setColor(ElPalette::Border, normal);
    } else if (isPlain) {
        _palette.setColor(ElPalette::Background, normal);
        _palette.setColor(ElPalette::Foreground, Qt::white);
        _palette.setColor(ElPalette::Border, normal);
    } else {
        _palette.setColor(ElPalette::Background, entered);
        _palette.setColor(ElPalette::Foreground, Qt::white);
        _palette.setColor(ElPalette::Border, entered);
    }

    _palette.toggleColorGroup(ElPalette::Pressed);
    if (isDefault) {
        _palette.setColor(ElPalette::Background, isPlain ? Qt::white : shallow);
        _palette.setColor(ElPalette::Foreground, deep);
        _palette.setColor(ElPalette::Border, deep);
    } else {
        _palette.setColor(ElPalette::Background, deep);
        _palette.setColor(ElPalette::Foreground, Qt::white);
        _palette.setColor(ElPalette::Border, deep);
    }

    _palette.toggleColorGroup(ElPalette::Disabled);
    if (isDefault) {
        _palette.setColor(ElPalette::Background, Qt::white);
        _palette.setColor(ElPalette::Foreground, ThemeColor::textDisabledColor);
        _palette.setColor(ElPalette::Border, ThemeColor::lineDisabledColor);
    } else if (isPlain) {
        _palette.setColor(ElPalette::Background, shallow);
        _palette.setColor(ElPalette::Foreground, disabled);
        _palette.setColor(ElPalette::Border, disabled);
    } else {
        _palette.setColor(ElPalette::Background, disabled);
        _palette.setColor(ElPalette::Foreground, Qt::white);
        _palette.setColor(ElPalette::Border, disabled);
    }
    // don't forget to reset colorGroup!
    _palette.toggleColorGroup(ElPalette::Normal);
}

ElCircleButton::ElCircleButton(QWidget *parent)
    : ElButton{parent}
{
    resize(30, 30);
}

ElCircleButton::ElCircleButton(ElIcon *icon, QWidget *parent)
    : ElCircleButton{parent}
{
    setIcon(icon);
}

void ElCircleButton::setIcon(ElIcon *icon)
{
    if (_icon == icon)
        return;
    delete _icon;
    _icon = icon;
    if (_icon != nullptr)
        _icon->fillBackground(_palette.color(ElPalette::Foreground));
    update();
}

void ElCircleButton::setIcon(const QString &filename, int iconSize)
{
    auto icon = new ElIcon(filename, this);
    icon->resize(iconSize, iconSize);
    setIcon(icon);
}

void ElCircleButton::paintEvent(QPaintEvent *e)
{
    resize(2.2 * _radius, 2.2 * _radius);
    QPainter painter(this);
    QPen pen;
    pen.setColor(_palette.color(ElPalette::Border));
    painter.setPen(pen);
    painter.setBrush(_palette.color(ElPalette::Background));
    painter.drawEllipse(getDrawRect().center(), _radius, _radius);
    if (_icon != nullptr) {
        auto r = _icon->rect();
        r.moveCenter(rect().center());
        _icon->move(r.topLeft());
        _icon->show();
    }
    e->accept();
}

qreal ElCircleButton::radius() const
{
    return _radius;
}

void ElCircleButton::setRadius(qreal radius)
{
    if (qFuzzyCompare(_radius, radius))
        return;
    _radius = radius;
    emit radiusChanged(_radius);
}

bool ElTextButton::isClipped() const
{
    return _clipped;
}

void ElTextButton::setClipped(bool clipped)
{
    if (_clipped == clipped)
        return;
    _clipped = clipped;
    update();
}

void ElTextButton::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    QRectF br; // bounding rect
    // we don't care about the color or style of the pen, just want to get the bounding rect.
    painter.drawText(QRect(), Qt::AlignCenter, _text, &br);
    bool hasIcon = (_icon != nullptr);
    auto w = br.width() + (_clipped ? 2 : 2 * Padding_Lr)
             + (hasIcon ? Icon_Size + (_text.isEmpty() ? 0 : Spacing) : 0);
    auto h = br.height() + (_clipped ? 0 : 2 * Padding_Tb) + (hasIcon ? Icon_Size : 0);
    resize(w, h); // now it is wide enough to show text completely.

    painter.setPen(_palette.color(ElPalette::Foreground));
    if (hasIcon) {
        bool isAppend = _iconStyle == ElAbstractButton::Append;
        auto x = (_clipped ? 0 : Padding_Lr)
                 + (isAppend ? 0 : Icon_Size + (_text.isEmpty() ? 0 : Spacing));
        QRect drawingRect(x, 0, br.width(), height());
        painter.drawText(drawingRect, Qt::AlignCenter, _text);
        if (isAppend)
            _icon->move(width() - Icon_Size - (_clipped ? 0 : Padding_Lr),
                        (height() - Icon_Size) / 2);
        else
            _icon->move(_clipped ? 0 : Padding_Lr, (height() - Icon_Size) / 2);
        _icon->show();
    } else {
        painter.drawText(getDrawRect(), Qt::AlignCenter, _text);
    }
    e->accept();
}

void ElTextButton::initPalette()
{
    auto normal = _theme.normalColor(), deep = _theme.deepColor();
    auto entered = _theme.enteredColor(), disabled = _theme.disabledColor();
    bool isDefault = (_theme.type() == ElTheme::Default);
    _palette.setColor(ElPalette::Foreground, isDefault ? ThemeColor::textColor : normal);
    _palette.toggleColorGroup(ElPalette::Entered);
    _palette.setColor(ElPalette::Foreground, isDefault ? normal : entered);
    _palette.toggleColorGroup(ElPalette::Pressed);
    _palette.setColor(ElPalette::Foreground, deep);
    _palette.toggleColorGroup(ElPalette::Disabled);
    _palette.setColor(ElPalette::Foreground, isDefault ? ThemeColor::textDisabledColor : disabled);
    _palette.toggleColorGroup(ElPalette::Normal);
}

void ElLink::initPainter(QPainter *painter) const
{
    ElTextButton::initPainter(painter);
    auto cg = _palette.currentColorGroup();
    auto font = painter->font();
    font.setUnderline(_hasUnderline && (cg == ElPalette::Entered || cg == ElPalette::Pressed));
    painter->setFont(font);
}

void ElLink::mouseReleaseEvent(QMouseEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    auto pos = e->pos();
    if (pos.x() >= 0 && pos.y() >= 0 && pos.x() <= width() && pos.y() <= height()) {
        if (!_target.isValid()) {
            qDebug() << "The target url is invalid.";
            e->accept();
            return;
        }
        QDesktopServices::openUrl(_target);
        emit clicked();
    }
    e->accept();
}

bool ElLink::hasUnderline() const
{
    return _hasUnderline;
}

void ElLink::setUnderline(bool b)
{
    _hasUnderline = b;
}

QUrl ElLink::target() const
{
    return _target;
}

void ElLink::setTarget(const QUrl &target)
{
    if (_target == target)
        return;
    _target = target;
    emit targetChanged(_target);
}
